package com.yuehuanghun.framework.web;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.yuehuanghun.framework.exception.BizException;
import com.yuehuanghun.framework.exception.FileUploadFailException;
import com.yuehuanghun.framework.exception.ImportantBizException;
import com.yuehuanghun.framework.exception.NotFoundException;
import com.yuehuanghun.framework.exception.ValidateException;
import com.yuehuanghun.framework.json.JSON;

@SuppressWarnings("rawtypes")
public class ApiExceptionAdvice extends BaseResource {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @ExceptionHandler(ValidateException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleValidateException(ValidateException ex) {
        logger.debug("", ex);
        RequestContextExceptionHolder.setException(ex);
        return makeResult(ex.getCode(), ex.getMessage(), ex.getErrors());
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleBindExceptionException(BindException ex) {
        RequestContextExceptionHolder.setException(ex);
        return extractMsg(ex.getBindingResult());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleMethodArgumentException(MethodArgumentNotValidException ex) {
        RequestContextExceptionHolder.setException(ex);
        return extractMsg(ex.getBindingResult());
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleValidateException(ConstraintViolationException ex) {
        RequestContextExceptionHolder.setException(ex);
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        String message = null;
        if(constraintViolations != null && !constraintViolations.isEmpty()) {
            message = constraintViolations.iterator().next().getMessage();
        }else {
            message = "数据验证失败";
        }

        return makeResult(HttpStatus.BAD_REQUEST.value(), message, null);
    }

    /**
     * 处理参数有误,json不能正确转换为对象异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleHttpMessageConversionException(HttpMessageConversionException ex) {
        RequestContextExceptionHolder.setException(ex);
        logger.error(ex.getMessage(), ex);
        String msg;
        Throwable throwable = ex.getCause();
        if (throwable instanceof InvalidFormatException) {
            InvalidFormatException ex2 = (InvalidFormatException) throwable;
            msg = String.format("参数类型不匹配{%s},required：{%s}", ex2.getValue(), ex2.getTargetType().getName());
        } else if (throwable instanceof JsonParseException) {
            JsonParseException ex2 = (JsonParseException) throwable;
            msg = String.format("参数有误,json数据格式有误 %s", ex2.getOriginalMessage());
        } else if (throwable instanceof JsonMappingException) {
            msg = "参数有误,参数格式错误,转换失败 ";
        } else {
            msg = String.format("参数有误  %s", ex.getMessage());
        }
        return makeResult(400, msg, null);
    }

    /**
     * 对业务异常的处理
     * @param ex
     * @return
     * @author yadmin
     * @date 2016年7月6日
     */
    @ExceptionHandler(ImportantBizException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper<Object> handleImportantBizException(ImportantBizException ex) {
        RequestContextExceptionHolder.setException(ex);
        logger.error(ex.getMessage(), ex);
        return makeResult(ex.getCode(), ex.getMessage() == null ? "系统异常" : ex.getMessage(), null);
    }
    
    /**
     * 对业务异常的处理
     * @param ex
     * @return
     * @author yadmin
     * @date 2016年7月6日
     */
    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper<Object> handleBizException(BizException ex) {
        RequestContextExceptionHolder.setException(ex);
        logger.warn(ex.getMessage(), ex);
        return makeResult(ex.getCode(), ex.getMessage() == null ? "系统异常" : ex.getMessage(), null);
    }
    
    @ExceptionHandler(NotFoundException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public ReturnWrapper handleNotFoundException(NotFoundException ex, HttpServletResponse response){
        RequestContextExceptionHolder.setException(ex);
    	logger.warn(ex.getMessage(), ex);
    	return makeResult(ex.getCode(), ex.getMessage(), null);
    }
    
    @ExceptionHandler(FileUploadFailException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ReturnWrapper handleFileUploadFailException(FileUploadFailException ex, HttpServletResponse response){
        RequestContextExceptionHolder.setException(ex);
    	logger.error(ex.getMessage(), ex);
    	return makeResult(ex.getCode(), ex.getMessage(), null);
    }

    /**
     * 统一处理其他异常，非RuntimeException
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ReturnWrapper handleAllException(Exception ex) {
        RequestContextExceptionHolder.setException(ex);
        logger.error(ex.getMessage(), ex);
        return makeResult(-1, "系统出现了一点小故障，请稍后重试", null);
    }

    protected ReturnWrapper extractMsg(BindingResult bindingResult) {
        Map<String,String> error = new HashMap<>();
        if (bindingResult.hasFieldErrors()) {
            List<FieldError> errors = bindingResult.getFieldErrors();
            for (FieldError err : errors) {
                error.put(err.getField(), err.getDefaultMessage());
            }
        }
        String message;
        if(error.isEmpty()) {
            message = "参数不正确!";
        } else if(error.size() == 1) {
            message = error.values().iterator().next();
        } else {
            message = "参数不正确!" + JSON.toJSONString(error);
        }
        return makeResult(HttpStatus.BAD_REQUEST.value(), message, null);
    }
    
    protected <T> ReturnWrapper<T> makeResult(int code, String msg, T res) {
        return new ReturnWrapper<>(code, msg, res);
    }
}
